/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#if !defined(AFX_GDIPINTERFACE_H__61081227_2FA9_4B5E_9293_7BA16162781F__INCLUDED_)
#define AFX_GDIPINTERFACE_H__61081227_2FA9_4B5E_9293_7BA16162781F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <math.h>
#include <afxpriv.h>
#include <objbase.h>
#include <boost/smart_ptr.hpp>

#ifndef max
	#define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef min
	#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#include <gdiplus.h>
#ifdef max
	#undef max
#endif
#ifdef min
	#undef min
#endif

#include <pgl/igfx/GfxInterface.h>
#include <pgl/igfx/GdiRect.h>

namespace gfxinterface
{
    class scoped_graphics_hdc
    {
    private:
        Gdiplus::Graphics* m_g;
        HDC m_hDC;
    public:

        explicit 
            scoped_graphics_hdc(Gdiplus::Graphics* g_)
            :m_g(g_),m_hDC(NULL)
        {
            if (!m_g)
                throw std::exception("graphics is null");
        }

        HDC get()
        {
            if (m_hDC == NULL)
                m_hDC = m_g->GetHDC();
            return m_hDC;
        }

        void reset()
        {
            if (m_g)
            {
                if (m_hDC)
                {
                    m_g->ReleaseHDC(m_hDC);
                }
                m_hDC = NULL;
            }
        }

        ~scoped_graphics_hdc()
        {
            reset();
        }

    };


/*! \brief GDI+ interface

  \ingroup IGfxGroup
*/
class CGDIpInterface : public CGfxInterface  
{
protected:
	//! A structure describing a linear gradient.
	struct SGDIpLinearGradient
	{
		Gdiplus::Color m_cStart;
		Gdiplus::Color m_cEnd;
		Gdiplus::PointF m_pStart;
		Gdiplus::PointF m_pEnd;
	};

	//! A structure describing a texture (for texture brush.
	struct SGDIpTexture
	{
		Gdiplus::RectF m_rDestRect;
		Gdiplus::WrapMode m_eWrapMode;
		Gdiplus::Bitmap* m_pBitmap;
	};

	//! GDI+ state
	struct SGDIpState
	{
		Gdiplus::GraphicsState m_gState;
		Gdiplus::Color m_curColor;
		static enum  EFillType {	SolidFill=0, LinearGradientFill=1, TextureFill=2};
		EFillType m_eFillType;
		Gdiplus::Color m_fillColor;
		SGDIpLinearGradient m_sLinearGradient;
		SGDIpTexture m_sTexture;
		Gdiplus::Color m_shadowColor;
		LOGFONT m_font;
		Gdiplus::REAL m_lineWidth;
		std::vector<Gdiplus::REAL> m_dash;
		Gdiplus::Matrix* m_pTransformMatrix;
		ELayer m_eLayer;
	};

	typedef std::stack<SGDIpState> SGdipStateStack;
	typedef std::list<Gdiplus::RectF> CRectFList;
    typedef boost::shared_ptr<const Gdiplus::Brush> brush_shared_ptr;

public:
	//! \name Constructors
	//@{
	CGDIpInterface();
	virtual ~CGDIpInterface();
	//@}

	void SetGraphics(Gdiplus::Graphics* pGraphics) 
    {	
        if(!pGraphics)
            throw std::exception("Graphics pointer is null"); 
        m_pGraphics = pGraphics;
    };
	virtual void BeginPaint(HDC hDC);
	virtual void EndPaint();

	virtual void PushClipWorld(double llx, double lly, double urx, double ury);
	virtual void PopClipWorld();

   //TODO: Figure out dynamic casting issue
   virtual void PushClipWorldD(double llx, double lly, double urx, double ury);
   virtual void PopClipWorldD();

	virtual void PushState();
	virtual void PopState();

	//! \name Translation and rotations
	//@{
	virtual void Rotate( double angleRad, double xOrig  = 0, double yOrig = 0);
	virtual void Scale( double xScale, double yScale, double xOrig = 0, double yOrig = 0);
	//@}

	virtual void AddComment(LPCTSTR str){};

	virtual void SetColor(double r, double g, double b, double a = 1) {	m_curState.m_curColor = Gdiplus::Color((BYTE)floor(a*255),(BYTE)floor(r*255),(BYTE)floor(g*255),(BYTE)floor(b*255));};
	virtual void SetFillColor(double r, double g, double b, double a = 1) {	m_curState.m_fillColor = Gdiplus::Color((BYTE)floor(a*255),(BYTE)floor(r*255),(BYTE)floor(g*255),(BYTE)floor(b*255)); m_curState.m_eFillType = SGDIpState::SolidFill;};
	virtual void SetLinearGradientFill(const SPointD& point1, const SPointD& point2, const SRGBA& col1, const SRGBA& col2);
	virtual void SetTextureFill(Gdiplus::Bitmap* pBitmap, const SRectD& rDestRect, ETextureWrapMode eWrapMode);
	virtual void SetShadowColor(double r, double g, double b, double a = 0.2)	{	m_curState.m_shadowColor = Gdiplus::Color((BYTE)floor(a*255),(BYTE)floor(r*255),(BYTE)floor(g*255),(BYTE)floor(b*255));};
	virtual void SetLineWidth(double width)	{	m_curState.m_lineWidth = (Gdiplus::REAL)__max(0,width);};	
	virtual void SetDash(LPCTSTR dash);
	virtual void SetLayer(ELayer eLayer)	{	m_curState.m_eLayer = eLayer;};
	ELayer GetLayer() const					{   return m_curState.m_eLayer;};

	virtual void DrawGridHor(double llx, double urx, double lly, double ury,double dy);
	virtual void DrawGridVer(double llx, double urx, double lly, double ury,double dx);

	virtual void DrawRect(double llx, double lly, double urx, double ury, DWORD dwRectFlag = 0, SPointD sShadowSize = SPointD(5,5));
	virtual void DrawRectStrip( const CVectorF& vX0, const CVectorF& vY0, const CVectorF& vWidth, const CVectorF& vHeight, DWORD dwRectFlag = RectTypeFilled);

	virtual void DrawArrow(double tailx, double taily, double tipx, double tipy, double tailthickness, double  headthickness, double headlength, bool filled =true);
	virtual void DrawArrowAbs(double tailx, double taily, double dtipx, double dtipy, double tailthickness, double  headthickness, double headlength, bool filled =true);
	virtual void DrawLine(double x1, double y1, double x2, double y2);

	virtual void DrawLineStrip(const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag =0 , double dTension=0);
	virtual void DrawStepStrip(const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag =0 );
	virtual void DrawBandStrip(const CVectorF& vX, const CVectorF& vYBottom, const CVectorF& vYTop, DWORD eBandTypeFlag = 0, double dTension=0);
	virtual void DrawMultipleLineStrip(int ns, const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag =0 );

	virtual void DrawStock( double x0, double dx, const CVectorF& vHis, const CVectorF& vLows,  const CVectorF& vOpens, const CVectorF& vCloses, const SRGBA& cDownColor, DWORD eStockTypeFlag = 0);
	virtual void DrawErrorBars( const CVectorF& vXs,  const CVectorF& vYs,const CVectorF& vUps, const CVectorF& vDowns, double dWidth, bool bAbsolute = true, bool bVertical = true);

	virtual void DrawCircle(double cx, double cy, double radius, DWORD eLineType = LineTypeClosed);
	virtual void DrawPolygon(double cx, double cy,  const CVectorF& vPx, const CVectorF& vPy, DWORD eLineType = LineTypeClosed);

	virtual void DrawCircleStrip(const CVectorF& vCx, const CVectorF& vCy, double radius, DWORD eLineType = LineTypeClosed);
	virtual void DrawPolygonStrip(const CVectorF& vCx, const CVectorF& vCy, const CVectorF& vPx, const CVectorF& vPy, DWORD eLineType = LineTypeClosed);

	virtual void SetFont(const LOGFONT& logFont);
	virtual void DrawText(double x, double y, LPCTSTR str, ETextAlignment ta = TextAlignmentLeft, double angle = 0);
	virtual void DrawTextStrip(const std::vector<SPointD>& vPoints, const std::vector<std::_tstring>& lStrings, ETextAlignment ta = TextAlignmentCenter, double angle = 0);

	//! \arc functions
	//@{
	virtual void DrawArc(double cnx, double cny, double rn, double angle, double aperture, double rep, DWORD flags);
	virtual void DrawDonnut(double cnx, double cny, double rin, double rout, double angle, double aperture, double rep, DWORD flags);
	//@}

	virtual void DrawBitmap(  Gdiplus::Bitmap*  pBitmap, const SRectI& rOrigBitmap, const SRectD& rDestBitmap);

	virtual void DrawSurf(const contour::TLattice<float>& tLattice, const CVectorF& vColor, DWORD dwFlag);
	virtual void DrawVectorField(const contour::TLattice<float>& tLattice, const CVectorF& vVx, const CVectorF& vVy, float fScale=1, DWORD dwFlags = 0);

	virtual double GetYDirectionUp() const	{	return -1;};

	virtual double BBToWorldx(double x) const
	{	return GetWorldLeft()+(x-(double)GetBBLeft())/(double)(GetBBWidth())
			*(double)(GetWorldWidth());};
	virtual double BBToWorldy(double y) const
	{	return GetWorldTop()+(y-(double)GetBBTop())/(double)(GetBBHeight())
			*(double)(GetWorldHeight());};
	virtual double BBToWorldAbsx(double x) const
	{	return x/(double)(GetBBWidth())*(double)(GetWorldWidth());};
	virtual double BBToWorldAbsy(double y) const
	{	return y/(double)(GetBBHeight())*(double)(GetWorldHeight());};

	//! Transform world x coordinate to file coordinate x
	virtual double WorldToBBx(double x) const
	{	return GetBBLeft()+(x-(double)GetWorldLeft())/(double)(GetWorldWidth())
			*(double)(GetBBWidth());};
	//! Transform world y coordinate to file coordinate y
	virtual double WorldToBBy(double y) const
	{	return GetMainBBBottom() + GetMainBBHeight() 
		- ((y-(double)GetWorldBottom())/(double)(GetWorldHeight())
			*(double)(GetBBHeight()) + GetBBBottom());};

	//! Transform world x coordinate to file coordinate x
	virtual double WorldToBBAbsx(double x) const
	{	return x/(double)(GetWorldWidth())*(double)(GetBBWidth());};
	//! Transform absolute world y coordinate to file coordinate y
	virtual double WorldToBBAbsy(double y) const
	{	return y/(double)(GetWorldHeight())*(double)(GetBBHeight());};

	virtual double NormBBoxToBBy(double y) const			{	return GetMainBBTop() - m_sCurBBox.screen.top + (1-y) * m_sCurBBox.screen.height;};
	virtual double NormClipBBoxToWorldy(double y) const		{	return BBToWorldy( GetMainBBTop()-m_rCurClip.top - (1-y) * m_rCurClip.height);};

	virtual double GetWorldClipBottom() const		{	return BBToWorldy(GetMainBBTop()-(m_rCurClip.top+m_rCurClip.height));};
	virtual double GetWorldClipTop() const			{	return BBToWorldy(GetMainBBTop()-m_rCurClip.top);};

	virtual double GetBBClipBottom() const		{	return m_rCurClip.top+m_rCurClip.height;};
	virtual double GetBBClipTop() const			{	return m_rCurClip.top;};

protected:
	// return pointer to current brush...
	brush_shared_ptr GetCurrentBrush();
	void GenerateFont(bool bForceGeneration = false);	
	static Gdiplus::WrapMode GetWrapMode(ETextureWrapMode eWrapMode);
	void ResizePoints(UINT iNewSize);
	void SetCurPenState(Gdiplus::Pen& pen);


    Gdiplus::Graphics* m_pGraphics;
    Gdiplus::Font* m_pFont;
	SGDIpState m_curState;
	SGdipStateStack	m_lStates;
private:
    std::vector<Gdiplus::REAL> m_pDash;
	UINT m_iPointsSize;
    std::vector<Gdiplus::PointF> m_pPoints;
};

}

#endif // !defined(AFX_GDIPINTERFACE_H__61081227_2FA9_4B5E_9293_7BA16162781F__INCLUDED_)
